
#ifndef MSGUTILS_BIT_READER_INL_H
#define MSGUTILS_BIT_READER_INL_H

#include <type_traits>

template<class T, bool is_arithmetic>
struct ReadDispatch {
  static void Read(BitReader& reader, T& val) { val->Read(reader); }
};

template<class T>
struct ReadDispatch<T, true> {
  static void Read(BitReader& reader, T& val) { 
    reader.DecodeNumber(sizeof(T) * 8, &val, true); 
  }
};

template<size_t size>
BitReader::BitReader(const uint8_t (&buffer)[size])
    : rbuf_(NULL), delete_rbuf_(false) {
  Construct(buffer, size);
}

template<class T>
BitReader& BitReader::operator>>(T& val) {
  Read(val);
  return *this;
}

template<size_t size>
void BitReader::Read(char (&string)[size]) {
  size_t len;
  size_t bytes = DecodeNumber(sizeof(size_t) * 8, &len, false);
  if (size < len + 1)
    throw insufficient_buffer_to_receive();

  rbuf_->Drain(bytes);
  rbuf_->Read((uint8_t*)string, len);
  string[len] = '\0';
}

template<class T>
void BitReader::Read(T& val) {
  ReadDispatch<T, std::is_arithmetic<T>::value>::Read(*this, val);
}

template<class T>
size_t BitReader::GetBlock(T* block, size_t size) {
  size_t real_size;
  size_t bytes = DecodeNumber(sizeof(size_t) * 8, &real_size, false);
  if (real_size > size)
    throw insufficient_buffer_to_receive();

  rbuf_->Drain(bytes);
  for (size_t i = 0; i < real_size; ++i) 
    Read(block[i]);
  return real_size;
}

#endif // MSGUTILS_BIT_READER_INL_H

